package com.hyt.it.ogt.kq.service.gov.photo.type;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.imageio.ImageIO;

import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.progress.service.ProgressManager;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.config.ConfigManager;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.exception.GovInvalidRequestException;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.utils.HuaweiCloudObsUtil;
import com.hyt.it.ogt.kq.service.gov.den.TencentCloudCosUtil;
import com.hyt.it.ogt.kq.service.gov.feign.tps.GovTpsClient;
import com.hyt.it.ogt.kq.service.gov.mapper.CandidateMapper;
import com.hyt.it.ogt.kq.service.gov.model.param.CandidateFileParam;
import com.hyt.it.ogt.kq.service.gov.photo.AbstractPhotoTemplate;
import com.hyt.it.ogt.kq.service.gov.photo.PhotoTypeEnum;
import com.hyt.it.ogt.kq.service.gov.service.ICandidatePhotoService;
import com.hyt.it.ogt.kq.service.gov.service.ICandidateService;
import com.hyt.it.ogt.kq.service.model.entity.Candidate;
import com.hyt.it.ogt.kq.service.model.entity.CandidatePhoto;
import com.hyt.loginfo.service.IBaseLoginService;
import com.hyt.model.tps.vo.Bucket;

import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.FileHeader;

/**
 * 考生个人照
 *
 * @author huangyh
 * @since 2021-03-25
 */
@Slf4j
@EqualsAndHashCode(callSuper = false)
@Component
public class PersonalPhoto extends AbstractPhotoTemplate {

    @Resource
    private CandidateMapper candidateMapper;

    @Autowired
    private ICandidateService iCandidateService;

    @Autowired
    private ICandidatePhotoService iCandidatePhotoService;

    @Resource
    private ConfigManager configManager;

    @Resource
    private GovTpsClient tpsClient;

    @Resource
    private IBaseLoginService iBaseLoginService;

    /**
     * 最小宽度
     */
    protected int minWidth = 1;

    /**
     * 最大宽度
     */
    protected int maxWidth = 413;

    /**
     * 最小高度
     */
    protected int minHeight = 1;

    /**
     * 最大高度
     */
    protected int maxHeight = 626;

    /**
     * 最小大小(kb)
     */
    protected float minSize = 30;

    /**
     * 最大大小(kb)
     */
    protected float maxSize = 120;

    /**
     * 后缀名
     */
    protected String suffix = ".jpg";

    /**
     *业务目录
     */
    protected String businessDirectory = "/candidate/";

    @Override
    protected void checkPhoto(MultipartFile file) throws GovInvalidRequestException, IOException {
        //校验图片大小（kb）
        this.checkPhotoSize(file);
        //校验图片尺寸（宽高）
        this.checkPhotoMeasure(file);
    }

    @Override
    protected CandidateFileParam uploadPhoto(MultipartFile file, String taskId) throws IOException {
        //: 上传照片需要修改为上传至腾讯云COS，并且定义的上传路径也不正确
        String str = file.getOriginalFilename();
        //生成照片名称
        String fileName = UUIDUtils.newSortUUID() + suffix;
        String filePath = configManager.getCloudRootPath() + taskId + businessDirectory + fileName;
        String path = HuaweiCloudObsUtil.uploadFile(filePath, file.getInputStream());
        CandidateFileParam candidateFileParam = new CandidateFileParam();
        candidateFileParam.setRelativePath(filePath);
        candidateFileParam.setName(fileName)
                .setPath(path)
                .setType(PhotoTypeEnum.PERSONAL.getCode());
        return candidateFileParam;
    }

    @Override
    protected Map<String, byte[]> checkBatchFile(String taskId, MultipartFile file, List<Candidate> withoutPhotoList) throws KqException, IOException {
        //校验导入的个人照片包
        //判断文件是否存在
        if (file.isEmpty()) {
            //导入的文件不存在
            BusinessLogicException.throwException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "导入的文件不存在");
        }
        //判断文件后缀zip，其他格式禁止导入
        String originalFileName = file.getOriginalFilename();
        final String fileFormat =".zip";
        if (originalFileName == null || !originalFileName.endsWith(fileFormat)) {
            //导入的文件后缀名不正确
            BusinessLogicException.throwException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "导入的文件后缀名不正确");
        }
        //创建临时文件
        File tempFile = File.createTempFile("personalPhoto_" + UUIDUtils.newSortUUID(), ".zip");
        log.info("# 批量导入个人照片包临时文件所在本地路径： {}", tempFile.getCanonicalPath());
        file.transferTo(tempFile);
        tempFile.deleteOnExit();
        //校验是否为合法的zip文件
        //考生、图片文件Map
        Map<String, byte[]> result = new HashMap<>(2);
        try (ZipFile zipFile = new ZipFile(tempFile)){
        	if (!zipFile.isValidZipFile()) {
                //导入的不是合法的zip文件
                //throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "导入的不是合法的zip文件");
                BusinessLogicException.throwException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "导入的不是合法的zip文件");
            }
            //获取考试任务下所有考生
            List<Candidate> candidateList = candidateMapper.getCandidateByTaskId(taskId);
            //获取已上传个人照片的考生
            List<Candidate> withPhotoList = candidateMapper.getCandidateWithPersonalPhoto(taskId, PhotoTypeEnum.PERSONAL.getCode());
            //获取未上传个人照片的考生
            List<Candidate> without = candidateList.stream()
                    .filter(candidate -> withPhotoList.stream()
                            .noneMatch(withPhoto -> withPhoto.getId().equals(candidate.getId())))
                    .collect(Collectors.toList());
            withoutPhotoList.addAll(without);
            //校验个人照片包内容
            List<FileHeader> fileHeaders = zipFile.getFileHeaders();
            ProgressManager.updateProgressCurrent(18L, "文件读取成功");
            // 文件数据验证
            photoFileValid(fileHeaders,candidateList,zipFile,result);
            ProgressManager.updateProgressCurrent(33L, "文件读取成功");
		} catch (Exception e) {
			log.error("# # 批量导入个人照片包临时文件所在本地路径异常", e);
		}
        return result;
    }

    /**
     * 文件数据验证
     * @param fileHeaders
     * @param candidateList
     * @param zipFile
     * @param result
     */
    public void photoFileValid(  List<FileHeader> fileHeaders , List<Candidate> candidateList ,ZipFile zipFile ,Map<String, byte[]> result  ){
        for(FileHeader fileHeader : fileHeaders){
            String fileName = fileHeader.getFileName();
            InputStream inputStream = null;
            //筛选出所有非目录的文件
            String filter = "/";
            if (!filter.equals(fileName.substring(fileName.length() - 1))) {
                //校验是否是图片文件
                BufferedImage bi = null;
                try {
                    inputStream = zipFile.getInputStream(fileHeader);
                    bi = ImageIO.read(inputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                //校验个人照片包中的图片名称的证件号是否在考试任务中有对应的考生
                String idNumber = fileName.substring(0, fileName.lastIndexOf(".")).replaceAll("_(.*\\d)","");
                //校验错误前缀
                String errorMsgPreFix =  "证件号为“"+idNumber+ "”的考生照片文件";
                log.info("批量导入考生照片,当前证件号码：{}",idNumber);
                if (bi == null) {
                    //导入的包中存在不是图片的文件
                    //throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "不是有效的图片文件");
                    BusinessLogicException.throwException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + ":不是有效的图片文件");
                }
                //校验文件后缀名
                int index = fileName.lastIndexOf("/");
                if (index != -1) {
                    //截取文件名
                    fileName = fileName.substring(index);
                }
                if (!fileName.endsWith(suffix)) {
                    //导入的包中存在后缀名不正确的图片
                    //throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "后缀名不符合上传规范");
                    BusinessLogicException.throwException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + ":后缀名不符合上传规范");
                }

                //: 临时去除照片尺寸校验，适配深港澳大湾区金融科技师考试
                /*
                //校验图片尺寸（宽高）
                int width = bi.getWidth();
                int height = bi.getHeight();
                //校验宽度
                if (width > maxWidth || width < minWidth) {
                    //导入的包中存在图片宽度不在规定范围内的图片
                    throw new KwGovException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "宽度不在规定范围内，宽度应该是1~413像素之间。");
                }
                //校验高度
                if (height > maxHeight || height < minHeight) {
                    //导入的包中存在图片高度不在规定范围内的图片
                    throw new KwGovException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "高度不在规定范围内，高度应该是1~626像素之间。");
                }
                */

                //: 临时去除照片大小校验，适配深港澳大湾区金融科技师考试
                /*
                //校验图片文件大小
                long longSize = fileHeader.getUncompressedSize();
                Float size = FileUtil.longSizeToFloat(longSize);
                if (size.compareTo(minSize) < 0 || size.compareTo(maxSize) > 0) {
                    //导入的包中存在图片文件大小不在规定范围内的图片
                    throw new KwGovException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "大小不在规定范围内，大小应该是30~120KB之间。");
                }
                */

                if (candidateList.stream().noneMatch(candidate -> idNumber.equals(candidate.getIdNumber()))) {
                    //导入的包中存在非当前考试任务的考生 2.4.10要求不处理
                    //throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(),errorMsgPreFix + "不是当前考试任务的考生照片");
                    continue;
                }
                //校验个人照片包中的图片名称的证件号对应的考生是否已经上传个人照片
                //if (withPhotoList.stream().anyMatch(candidate -> idNumber.equals(candidate.getIdNumber()))) {
                    //导入的包中存在已经上传了个人照片的考生 -- 2.4.10要求取最新的
                    //throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), errorMsgPreFix + "已经上传，无法再次上传");
                //}
                //提取图片文件
                InputStream fileStream = null;
                try {
                    fileStream = zipFile.getInputStream(fileHeader);
                    byte[] bytes = IOUtils.toByteArray(fileStream);
                    result.put(fileName, bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (fileStream != null) {
                        try {
                            fileStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }


    @Override
    protected void uploadBatch(Map<String, byte[]> result, String taskId, List<Candidate> withoutPhotoList) throws KqException {
        //上传图片
        List<CandidatePhoto> candidatePhotoList = new ArrayList<>();
        String officeId = iBaseLoginService.getOfficeId();
        ApiResponse<Bucket> tempCredentialResponse = tpsClient.getTencentCosTempCredential(officeId);
        ProgressManager.updateProgressCurrent(40L, "文件开始上传");
        if (tempCredentialResponse.getCode() != 1) {
            //获取腾讯云COS临时凭证失败
            //throw new KqException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getCode(),ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getMsg());
            BusinessLogicException.throwException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getCode(), "批量导入考生照片异常");
        }
        String domain = tempCredentialResponse.getData().getDomain();
        Integer count = 1;
        for (Map.Entry<String, byte[]> entry : result.entrySet()) {
            String fileName = entry.getKey().replaceAll("_(.*\\d)","");
            byte[] file = entry.getValue();
            String idNumber = fileName.substring(0, fileName.lastIndexOf("."));
            String newFileName = UUIDUtils.newSortUUID() + suffix;
            //使用腾讯云COS上传
            String path = TencentCloudCosUtil.getPhotoRootPath(taskId) + newFileName;
            ApiResponse<String> uploadResponse = tpsClient.uploadTencentCosCompressedPhoto(officeId, path, file);
            if (uploadResponse.getCode() != 1) {
                //上传失败
                //throw new KqException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getCode(), ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getMsg());
                BusinessLogicException.throwException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_EXCEPTION.getCode(), "批量导入考生照片异常");
            }
            for (Candidate candidate : withoutPhotoList) {
                if (candidate.getIdNumber().equals(idNumber)) {
                    CandidatePhoto candidatePhoto = new CandidatePhoto();
                    candidatePhoto.setCandidateId(candidate.getId())
                            .setTaskId(candidate.getTaskId())
                            .setPhotoType(PhotoTypeEnum.PERSONAL.getCode())
                            .setPhotoPath(domain + "/" + uploadResponse.getData())
                            .setName(newFileName);
                    candidatePhotoList.add(candidatePhoto);
                    break;
                }
            }

            ProgressManager.updateProgressCurrent(55L, "文件开始完成");
            log.info("批量导入照片进度,总：{}，当：{}",result.size(),count++);
        }
        //插入考生照片表
        if (!iCandidatePhotoService.saveBatch(candidatePhotoList)) {
            //throw new KqException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_FAIL.getCode(), ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_FAIL.getMsg());
            BusinessLogicException.throwException(ResponseCode.ERROR_BATCH_IMPORT_CANDIDATE_PHOTO_FAIL.getCode(), "插入考生照片失败");
        }
        ProgressManager.updateProgressCurrent(75L, "文件入库完成");
    }

    /**
     * 校验图片大小（kb）
     * @throws KqException 校验不通过，抛出异常
     */
    protected void checkPhotoSize(MultipartFile file) throws KqException, IOException {
        //校验图片文件是否存在
        if (file.isEmpty()) {
            //图片文件为空
            throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "图片文件为空");
        }
        //校验是否是图片文件
        BufferedImage bi = ImageIO.read(file.getInputStream());
        if (bi == null) {
            //上传的文件不是图片
            throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "上传的文件不是图片");
        }
        //校验图片后缀
        String fileName = file.getOriginalFilename();
        if (fileName == null || !fileName.endsWith(suffix)) {
            //图片文件后缀名不正确
            throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "图片文件后缀名不正确");
        }
        //: 临时去除照片大小校验，以此适配深港澳大湾区金融科技师考试
        /*
        //校验图片文件大小
        Float size = FileUtil.getSizeInKb(file);
        if (size.compareTo(minSize) < 0 || size.compareTo(maxSize) > 0) {
            //图片文件大小不在规定范围内
            throw new KwGovException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "图片文件大小不在规定范围内");
        }
        */
    }

    /**
     * 校验图片尺寸（宽高）
     * @throws KqException 校验不通过，抛出异常
     */
    protected void checkPhotoMeasure(MultipartFile file) throws KqException, IOException {
        BufferedImage bi = ImageIO.read(file.getInputStream());
        int width = bi.getWidth();
        int height = bi.getHeight();
        //校验宽度
        if (width > maxWidth || width < minWidth) {
            //图片宽度不在规定范围内
            throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "图片宽度不在规定范围内");
        }
        //校验高度
        if (height > maxHeight || height < minHeight) {
            //图片高度不在规定范围内
            throw new KqException(ResponseCode.COMMON_PARAM_INVALID.getCode(), "图片高度不在规定范围内");
        }
    }

}
